package p41_autoconfig;

import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;

import java.util.List;

/**
 * 这一节介绍springboot的自动配置原理
 * @Import会将指定类的对象注册进容器
 */
@Configuration
@Import(_1_AutoConfig.MyImportSelector.class)
public class _1_AutoConfig {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("autoConfig", _1_AutoConfig.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        //是否允许bean覆盖(本项目与第三方starter的bean冲突了，会优先使用本项目的,本质是先解析@Import，在解析本项目的bean，所以后解析的把先解析的覆盖了，下面配置可以设置不允许覆盖)
        context.getDefaultListableBeanFactory().setAllowBeanDefinitionOverriding(false);
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

    static
    /**
     * ImportSelector的实现类，被注册进容器后，refresh时会自动调用实现类的selectImports方法
     * 该方法返回一个字符串数组，数组元素为class的全类名，springboot会将这些全类名对应的对象注册进容器
     * 加上这些全类名不用写死，而是放进配置文件，这里读取配置文件，就做到了解耦
     * 指定一个目录，规定第三方全类名的配置文件放在该目录下，springboot去从该目录下读取所有配置文件，解析到处全类名并返回
     * 不就可以完成第三方starter自动装配了吗?
     */
    public class MyImportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            //类路径下的META-INFO文件夹的spring.factories文件里内容是key(类或接口的全类名)和value(也是类的全类名)
            //该工具类可以根据class类(会转化成key)，得到对应所有的类的全类名，并将这些类注册进容器
            List<String> classes = SpringFactoriesLoader.loadFactoryNames(ImportSelector.class, null);
            return classes.toArray(new String[0]);
        }
    }

    //如果实现接口是DeferredImportSelector，则会推迟创建自动装配的bean，优先创建本项目的bean
    //可以配合条件注解，如果本项目有某个bean，就使用本项目的，否则使用第三方starter的某个
    public class MyDeferredImportSelector implements DeferredImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            //类路径下的META-INFO文件夹的spring.factories文件里内容是key(类或接口的全类名)和value(也是类的全类名)
            //该工具类可以根据class类(会转化成key)，得到对应所有的类的全类名，并将这些类注册进容器
            List<String> classes = SpringFactoriesLoader.loadFactoryNames(ImportSelector.class, null);
            return classes.toArray(new String[0]);
        }
    }
}
